#include "Graphics.h"
#include <Windows.h>

#ifdef _DEBUG
#ifndef HR

#define HR(x)					\
	{								\
	HRESULT hr = x;				\
	if (FAILED(hr))				\
			{						\
			DXTRACE_ERR(#x, hr);	\
			DebugBreak();			\
			}						\
	}
#endif
#else
#ifndef HR
#define HR(x) x;
#endif
#endif

Graphics::Graphics()
	: m_iHeight(0), m_iWidth(0)
	, m_bInitialized(false)
	, m_bInitFailed(true)
	, m_bWindowed(true)
	, m_iNumLines(0)
	, m_bLineStarted(false)
{}

/**
* This function need only be called once during the program
* It calls sub-routines that initializes various aspects of Graphics
*
* @param hWnd is passed so that the device knows which window to draw to
* @return wether or not the function ran without error
*/
bool Graphics::Init(HWND hWnd)
{

	if(m_bInitialized)					// prevent this function from being called more than once
		return !m_bInitFailed;
	m_bInitialized = true;				// flag that this function has been called

	/* if at any point a function fails, return false */
	if(!InitDevice(hWnd))
		return false;
	if(!InitManagers())
		return false;
	if(!InitCamera())
		return false;
	if(!InitLight())
		return false;
	if(!InitMaterials())
		return false;

	m_DebugLines[0] = D3DXVECTOR2(0.0f, 40.0f);
	m_ObjectColor = D3DCOLOR_XRGB(0,0,0);
	m_iNumLines++;
	m_bInitFailed = false;
	return true;
}

bool Graphics::InitDevice(HWND hWnd)
{
	/* Create a temporary IDirect3D9 object used to setup the device */
	IDirect3D9* pD3DObject = NULL;
	pD3DObject = Direct3DCreate9(D3D_SDK_VERSION);

	if(NULL == hWnd)
		return false;

	/* Setup a Rect with the window dimensions to make sure they have been set */
	RECT rClientRect;
	if(!::GetClientRect(hWnd, &rClientRect))
		throw "GetClientRect() failed";
	g_scrHeight = m_iHeight = (int)(rClientRect.bottom - rClientRect.top);
	g_scrWidth  = m_iWidth  = (int)(rClientRect.right -  rClientRect.left);

	m_viewPort.X = m_viewPort.Y = 0;
	m_viewPort.Width =  g_scrWidth;
	m_viewPort.Height = g_scrHeight;
	m_viewPort.MinZ = 0.0f;
	m_viewPort.MaxZ = 1.0f;


	D3DCAPS9 D3DCaps;

	HR(pD3DObject->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &D3DCaps));
	//D3DCaps.TextureCaps = D3DPTEXTURECAPS_NONPOW2CONDITIONAL;

	// determine what type of vertex processing to use
	DWORD dwBehaviorFlags = 0;

	if(D3DCaps.VertexProcessingCaps != 0)
		dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;	

	//set up present parameters
	memset(&m_D3Dpp, 0, sizeof(m_D3Dpp));
	m_D3Dpp.hDeviceWindow			= hWnd;
	m_D3Dpp.Windowed				= true;				// SET TRUE FOR WINDOWED MODE
	m_D3Dpp.EnableAutoDepthStencil	= true;
	m_D3Dpp.AutoDepthStencilFormat	= D3DFMT_D16;
	m_D3Dpp.BackBufferCount			= 1;
	m_D3Dpp.BackBufferFormat		= D3DFMT_X8R8G8B8;
	m_D3Dpp.BackBufferWidth			= g_scrWidth;
	m_D3Dpp.BackBufferHeight		= g_scrHeight;
	m_D3Dpp.SwapEffect				= D3DSWAPEFFECT_DISCARD;
	m_D3Dpp.PresentationInterval	= D3DPRESENT_INTERVAL_DEFAULT;
	m_D3Dpp.Flags					= D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
	m_D3Dpp.MultiSampleQuality		= 0;
	m_D3Dpp.MultiSampleType			= D3DMULTISAMPLE_NONE;
	// setup the d3ddevice using the d3dobject and present parameters
	HRESULT hr = pD3DObject->CreateDevice(D3DADAPTER_DEFAULT, 
		D3DDEVTYPE_HAL, 
		hWnd,
		dwBehaviorFlags, 
		&m_D3Dpp,
		&m_pD3DDevice);
	pD3DObject->Release();
	pD3DObject = NULL;

	assert(hr == D3D_OK && "The device is broken");
	if(hr != D3D_OK)
		return false;

	m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
	//m_pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE,true );
	//m_pD3DDevice->SetRenderState(D3DRS_ZFUNC,D3DCMP_LESSEQUAL );

	//m_pD3DDevice->SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE );

	return true;
}

bool Graphics::InitManagers()
{
	HRESULT hr = D3DXCreateFont(m_pD3DDevice, 15, 0, NULL, 0, false,
		DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
		DEFAULT_PITCH | FF_DONTCARE, TEXT("Consolas"), &m_pFontManager);

	hr = D3DXCreateSprite(m_pD3DDevice, &m_pSpriteManager);

	D3DXCreateLine(m_pD3DDevice, &m_pLineManager);
	return true;
}

bool Graphics::InitCamera()
{
	// setup the perspective matrix
	D3DXMatrixPerspectiveFovLH(&m_matPerspective,		// the output 
		D3DXToRadian(65),								// field of view
		(float)m_iWidth / (float)m_iHeight,				// aspect ratio
		0.1f, 10000.0f);									// near plane and far plane

	// setup the view matrix
	D3DXMatrixLookAtLH(&m_matView, &m_Camera.eyePos, &m_Camera.lookAt, &m_Camera.upVector);
	m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &m_matPerspective);
	m_pD3DDevice->SetTransform(D3DTS_VIEW, &m_matView);
	return true;
}

bool Graphics::InitLight()
{
	//m_pD3DDevice->SetRenderState(D3DRS_SPECULARENABLE, TRUE);
	m_pD3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(30, 50, 50));

	ZeroMemory(&m_WorldLight, sizeof(D3DLIGHT9));
	ZeroMemory(&m_PlayerLight, sizeof(D3DLIGHT9));
	
	m_WorldLight.Type = D3DLIGHT_DIRECTIONAL;
	m_WorldLight.Ambient = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.00f);
	m_WorldLight.Diffuse = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.00f);		// color of the light
	m_WorldLight.Specular = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.00f);
	m_WorldLight.Position.x = 0;	
	m_WorldLight.Position.y = 1000;	
	m_WorldLight.Position.z = 10;
	D3DXVec3Normalize((D3DXVECTOR3*)&m_WorldLight.Direction, (D3DXVECTOR3*)&m_WorldLight.Position);
	//m_WorldLight.Range = 50.0f;
	//m_WorldLight.Falloff = 1.0f;
	

	m_PlayerLight = m_WorldLight;
	m_PlayerLight.Range = 150.f;
	m_PlayerLight.Ambient = D3DXCOLOR(1.0f, 0.5f, 0.5f, 1.0f);
	m_PlayerLight.Type = D3DLIGHT_POINT;
	m_PlayerLight.Position.y = 0;
	m_PlayerLight.Attenuation0 = 0.0f;
	m_PlayerLight.Attenuation1 = 0.05f;
	m_PlayerLight.Attenuation2 = 0.0f;

	m_pD3DDevice->SetLight(LIGHT_WORLD, &m_WorldLight);
	m_pD3DDevice->SetLight(LIGHT_PLAYER, &m_PlayerLight);
	m_pD3DDevice->LightEnable(LIGHT_WORLD, true);
	m_pD3DDevice->LightEnable(LIGHT_PLAYER, true);

	return true;
}

bool Graphics::InitMaterials()
{
	ZeroMemory(&m_DefaultMat, sizeof(D3DMATERIAL9));
	m_DefaultMat.Ambient = D3DXCOLOR(1, 1, 1, 1);
	m_DefaultMat.Diffuse = D3DXCOLOR(0, 0, 0, 1);
	//m_DefaultMat.Specular = D3DXCOLOR(0.1f, 0.1f, 0.1f, 1);
	m_DefaultMat.Power = 10;

	//m_pD3DDevice->SetRenderState(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL);

	return true;
}

/**
* Adds a single 2D object into the GameAsset list in Graphics
* 
* @param textureLoc is the filename of the asset
* @param cKey is the color key, put NULL if there isn't one
* @return a Vector2D, of which the x is which list and y is the index within that list
*/
Vector2D Graphics::AddAsset(const char* textureLoc, color* cKey, RECT* pBlitData, e_AssetType assetList)
{
	// search through the asset list and make sure the asset isn't already there
	for(int i = 0; i < m_vAssetLists[assetList].size(); ++i)
	{
		if(m_vAssetLists[assetList][i].cszTexFileName  == textureLoc)
			return Vector2D(assetList, i);
	}
	Vector2D returnVec(assetList, 0);

	// TODO: Check to see if the asset has been added before attempting to add it again
	LPDIRECT3DTEXTURE9 pTexture = NULL;

	LoadTextureFromFile(textureLoc, cKey, pTexture);

	// with the texture generated, create a game asset with it and it it to the list
	GameAsset* asset = new GameAsset(false, NULL, NULL, pTexture, textureLoc, pBlitData);

	returnVec.y = (float)m_vAssetLists[assetList].push(*asset);
	return returnVec;
}

/**
* Adds a single 3D object into the GameAsset list in Graphics
* 
* @param  meshLoc is the filename of the .x file
* @param textureLoc is the filename of the texture to be put on the mesh
* @return a Vector2D, of which the x is which list and y is the index within that list
*/
Vector2D Graphics::AddAsset(const char* meshLoc, const char* textureLoc, color* cKey, RECT* pBlitData, e_AssetType assetList)
{
	// search through the asset list and make sure the asset isn't already there
	for(int i = 0; i < m_vAssetLists[assetList].size(); ++i)
	{
		if(m_vAssetLists[assetList][i].cszMeshFileName == meshLoc &&
			m_vAssetLists[assetList][i].cszTexFileName  == textureLoc)
			return Vector2D(assetList, i);
	}

	Vector2D returnVec(assetList, 0);
	ID3DXMesh* pMesh = NULL;
	IDirect3DTexture9* pTexture = NULL;

	LoadTextureFromFile(textureLoc, cKey, pTexture);
	LoadMeshFromFile(meshLoc, pMesh);
	GameAsset* asset = new GameAsset(true, pMesh, meshLoc, pTexture, textureLoc, pBlitData);

	returnVec.y = (float)m_vAssetLists[assetList].push(*asset);
	return returnVec;
}
void Graphics::ChangeAssetColorKey(Vector2D assetLoc, color RGBval)
{
	if(m_vAssetLists->size())
		m_vAssetLists[int(assetLoc.x)][int(assetLoc.y)].Color = RGBval;
}

bool Graphics::RemoveAsset(Vector2D assetLoc)
{

	if(assetLoc.x < 0 || assetLoc.x >= ASSET_TYPES)
		return false;

	return m_vAssetLists[(int)assetLoc.x].remove((int)assetLoc.y);
}

Vector2D Graphics::GetAssetDimensions(Vector2D assetLoc)
{
	Vector2D dimensions(0, 0);
	LPDIRECT3DTEXTURE9 texture;
	D3DSURFACE_DESC desc;

	if(assetLoc.x < 0 || assetLoc.x >= ASSET_TYPES)
		texture = NULL;
	else
		texture = m_vAssetLists[(int)assetLoc.x].getByIndex((int)assetLoc.y).pObjTex;

	if(texture)
	{
		texture->GetLevelDesc(0, &desc);

		dimensions.x = (float)desc.Width;
		dimensions.y = (float)desc.Height;
	}

	return dimensions;
}

D3DXVECTOR3 Graphics::ConvertScreenToWorld(D3DXVECTOR2 mouseCoord)
{
	D3DXVECTOR3 v;
	D3DXMATRIX	VI;
	D3DXVECTOR3 rayOrigin, rayDir;

	// convert screen coordinates to projection coordinates
	float sx = mouseCoord.x;
	float sy = mouseCoord.y;
	v.x =  ( ( ( 2.0f * sx ) / g_scrWidth  ) - 1 ) / m_matPerspective._11;
	v.y = -( ( ( 2.0f * sy ) / g_scrHeight ) - 1 ) / m_matPerspective._22;
	v.z =  1.0f;

	D3DXMatrixInverse(&VI, NULL, &m_matView);

	// transform the screen space pick ray into 3D space
	rayDir.x  = v.x * VI._11 + v.y * VI._21 + v.z * VI._31;
	rayDir.y  = v.x * VI._12 + v.y * VI._22 + v.z * VI._32;
	rayDir.z  = v.x * VI._13 + v.y * VI._23 + v.z * VI._33;
	rayOrigin.x = VI._41;
	rayOrigin.y = VI._42;
	rayOrigin.z = 0.0f;//m._43;

	return rayOrigin;
}

D3DXVECTOR4 Graphics::Transform(D3DXVECTOR3 pos, D3DXMATRIX mat)
{
	D3DXVECTOR4 ret;
	ret.x = (pos.x*mat._11)+(pos.y*mat._21)+(pos.z*mat._31)+(1.0f*mat._41);
	ret.y = (pos.x*mat._12)+(pos.y*mat._22)+(pos.z*mat._32)+(1.0f*mat._42);
	ret.z = (pos.x*mat._13)+(pos.y*mat._23)+(pos.z*mat._33)+(1.0f*mat._43);
	ret.w = (pos.x*mat._14)+(pos.y*mat._24)+(pos.z*mat._34)+(1.0f*mat._44);

	return ret;
}

void Graphics::ConvertWorldToScreen(D3DXVECTOR3 pos)
{
	D3DXMATRIX world = CreateWorldMatrix(Vector3D(pos.x, pos.y, pos.z),
		Vector3D(0.0f, 0.0f, 0.0f),
		Vector3D(1.0f, 1.0f, 1.0f));
	//D3DXMATRIX WVP;
	//D3DXMatrixMultiply(&WVP, &world, &m_matView);
	//D3DXMatrixMultiply(&WVP, &WVP, &m_matPerspective);
	//WVP = world * m_matView * m_matPerspective;
	//D3DXVECTOR4 v4 = Transform(pos, WVP);
	//v4.x /= v4.w;
	//v4.y /= v4.w;
	//v4.y *= -1;
	//v4.x = v4.x*.5f + .5f;
	//v4.y = v4.y*.5f + .5f;

	//v4.x *= g_scrWidth;
	//v4.y *= g_scrHeight;

	D3DXVECTOR3 screenPos;

	D3DXVec3Project(&screenPos, &pos, &m_viewPort, &m_matPerspective, &m_matView, &world);
	screenPos;
}

void Graphics::Update()
{
	if(Engine::Ins()->input.mouseButtonHit(LMB))
	{
		// calculate where in world space the mouse clicked
		D3DXVECTOR3	pos;
		D3DXVECTOR3 worldPos;
		D3DVIEWPORT9 viewport;
		D3DXMATRIX world, proj, view;
		//D3DXMatrixTranslation(&world, 0.0f, 0.0f, 0.0f);
		m_pD3DDevice->GetViewport(&viewport);
		m_pD3DDevice->GetTransform(D3DTS_WORLD, &world);
		m_pD3DDevice->GetTransform(D3DTS_PROJECTION, &proj);
		m_pD3DDevice->GetTransform(D3DTS_VIEW, &view);
		pos.x = Engine::Ins()->input.mouseXPos();
		pos.y = Engine::Ins()->input.mouseYPos();
		pos.z = 1.0f;

		D3DXVec3Unproject(&worldPos, &pos, &viewport, &proj, &view, &world);
	}
}

/**
* Should be called in the game loop once; draws one frame
*
*/
void Graphics::Render()
{
	if(!m_bInitialized || m_bInitFailed)
		return;

	//m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET , D3DXCOLOR(0.2f, 0.2f, 0.2f, 0.0f), 1.0f, 0);
	m_pD3DDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0);
	m_pD3DDevice->BeginScene();

	/* Draw world objects */
	DrawWorld();

	/* Draw game objects */
	for(int i = 0; i < NUM_DRAWLISTS; ++i)
	{
		Vector<BaseObject*> drawList = Engine::Ins()->dlHandler.GetObjectDrawlist(i);
		// draw active draw loops
		for(int j = 0; j < drawList.size(); ++j)
			DrawObject(drawList.getByIndex(j));
	}

	m_pD3DDevice->EndScene();
	m_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}

/**
* Responsible for drawing all world blocks
* The process of doing so is parsing through the chunks to be drawn, and calculating their worldspace positions within the loop
*
*/
void Graphics::DrawWorld()
{
	/* local variables to make it easy on memory and processing */
	Vector<Block_Info*> * drawlist = &Engine::Ins()->dlHandler.GetWorldDrawlist();
	Block*		blockArray = NULL;
	GameAsset	asset;
	Vector3D	objPos;
	Vector2D	modifier;

	// for each chunk in the drawlist
	for(short i(0); i < drawlist->size(); ++i)
	{
		if((*drawlist)[i]->Data != NULL) {
			objPos.x = (float)(*drawlist)[i]->X;
			objPos.y = (float)(*drawlist)[i]->Y;
			objPos.z = 10.0f;
			asset = m_vAssetLists[ASSET_WORLD].getByIndex((*drawlist)[i]->Data->GetAssetID_s());
			Draw3D(objPos, Vector3D(), Vector3D(1.0f, 1.0f, 1.0f), asset);
		}
		else break;
	}

}

/**
* Responsible for drawing a single object
* @param pObj holds the address of the object
*/
void Graphics::DrawObject(BaseObject* pObj)
{
	// don't bother drawing if it's pointing to NULL
	if(pObj == NULL)
		return;

	// also don't bother drawing if the object isn't active or the objectType was never set
	if(pObj->active == false || pObj->objectType == ERROR_TYPE)
		return;

	Vector2D assetIndex = pObj->assetIndex;			// retrieve the object's asset index
	// using temp variables for easy use
	int list = (int)pObj->assetIndex.x;
	int pos =  (int)pObj->assetIndex.y;
	GameAsset asset = m_vAssetLists[list].getByIndex(pos);
	switch(pObj->objectType)
	{
	case TYPE_ATMOSPHERE:
	case TYPE_PLAYER_PIECE:
		SetCameraView(pObj->getPos().x,pObj->getPos().y+float(g_scrHeight*.01), pObj->getPos().z);	//Offset to look nice - Edit by York, Switched from .03 to .02 to avoid small graphic twitch at the top of the screen
		SetCameraPos(pObj->getPos().x, pObj->getPos().y+float(g_scrHeight*.03), m_Camera.eyePos.z );//Offset to look nice
		SetPlayerLight(pObj->getPos().x, pObj->getPos().y, -1/*pObj->getPos().z*/);
	case TYPE_ENTITY_PIECE:
		Draw3D(pObj->getPos(), pObj->getRotation(), pObj->getScale(), asset);
		break;
	case TYPE_GUI:
		{
			gui_panel* guiObj = ((gui_panel*)pObj);
			DrawGUI(guiObj, asset);
		}
		break;
	default:
		break;
	}
}

D3DXMATRIX Graphics::CreateWorldMatrix(Vector3D a_trans, Vector3D a_rot, Vector3D a_scale)
{
	D3DXMATRIX world, scale, trans, rot;

	D3DXMatrixScaling(&scale, a_scale.x, a_scale.y, a_scale.z);			// Scaling
	D3DXMatrixRotationYawPitchRoll(&rot, a_rot.x, a_rot.y, a_rot.z);	// Rotation
	D3DXMatrixTranslation(&trans, a_trans.x, a_trans.y, a_trans.z);		// Translating
	D3DXMatrixMultiply(&world, &scale, &rot);
	D3DXMatrixMultiply(&world, &world, &trans);
	return world;
}

void Graphics::Draw3D(Vector3D a_trans, Vector3D a_rot, Vector3D a_scale, GameAsset a_asset)
{ 

	m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

	m_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
	m_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
	m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); 
	m_pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); 
	m_pD3DDevice->SetRenderState(D3DRS_ALPHAREF, (DWORD)16); 
	m_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); 

#ifdef WIREFRAME 
	m_pD3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); 
#endif

	/*a_scale.x *=-1;
	a_scale.y *=-1;
	a_scale.z *=-1;*/
	//a_rot.z = 90;

	m_worldMat = CreateWorldMatrix(a_trans, a_rot, a_scale);

	if(a_asset.pObjTex != NULL)
		m_pD3DDevice->SetTexture(NULL, a_asset.pObjTex);				// set texture

#ifndef SILHOUETTE
	m_pD3DDevice->SetMaterial(&m_DefaultMat);							// set material
#endif
	m_pD3DDevice->SetTransform(D3DTS_WORLD, &m_worldMat);				// set world transform

	a_asset.pObjMesh->DrawSubset(0); 
}

void Graphics::DrawGUI(gui_panel* guiObj, GameAsset asset)
{
	if(asset.pObjTex == NULL) 
		return;

	m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
	m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

	Quad pos = guiObj->accessText(0).pos;
	float x = (float)pos.x;
	float y = (float)pos.y;
	char* val;

	//m_worldMat = CreateWorldMatrix(guiObj->getPos(),guiObj->getScale(),guiObj->getRotation());
	//m_pSpriteManager->SetTransform(&m_worldMat);    //Move the sprite into place

	//D3DXVECTOR3 drawPosition(guiObj->getPos().x,guiObj->getPos().y, 1.0f );

	/* The process of drawing a GUI object involves first drawing a texture, then drawing all the text within */

	D3DXMATRIX world, scale, trans, rot;
	D3DXMatrixIdentity		(&rot);
	D3DXMatrixScaling		(&scale, guiObj->getScale().x, guiObj->getScale().y, guiObj->getScale().z);
	D3DXMatrixTranslation	(&trans, guiObj->getPos().x  , guiObj->getPos().y  , 1.0f);
	D3DXMatrixMultiply		(&world,&rot,&trans);
	D3DXMatrixMultiply		(&world,&scale,&trans);

	m_pSpriteManager->SetTransform(&world);
	m_pSpriteManager->Begin(D3DXSPRITE_ALPHABLEND);

	if(asset.Color.red == -1)	m_ObjectColor = 0xFFFFFFFF;
	else						m_ObjectColor = D3DCOLOR_XRGB(asset.Color.red,asset.Color.green,asset.Color.blue);

	m_pSpriteManager->Draw(asset.pObjTex,asset.pBlitData,0,0,m_ObjectColor);  //Draw the sprite onto screen

	//m_pSpriteManager->Draw(asset.pObjTex,		// the texture to use
	//			asset.pBlitData,				// pSrcRect  - use the blit rect, if one exists
	//			NULL,							// pCenter   - NULL specifies the upper left hand corner
	//			&drawPosition,					// pPosition - position of the object
	//			m_ObjectColor);					// Color	 - don't modulate color, use default

	m_pSpriteManager->End();

	m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

	/* draw each set of texts that are a part of the GUI object */ 
	for(int i = 0; i < guiObj->numElements(); ++i)
	{
		pos = guiObj->accessText(i).pos;
		RECT rPos = { pos.x, pos.y, pos.getMaxX(), pos.getMaxY() };
		val = ((gui_panel*)(guiObj))->accessText(i).val;

		m_pFontManager->DrawTextA(NULL,			// pSprite   - we don't need this, set to NULL 
			val,							// pString   - the string to be drawn
			-1,								// Count	 - num of characters in the string, set to -1 to use entire thing
			&rPos,							// pRect	 - the rectangle location of the string
			DT_LEFT,						// Format	 - how to format the string
			0xFFFFFFFF);					// Color	 - don't modulate color, use default
	}

	//m_pSpriteManager->End();

}

void Graphics::LoadMeshFromFile(const char* cszFileName, ID3DXMesh* & pMesh)
{
	pMesh = NULL;

	/* Prepare buffers returned by D3DXLoadMeshFromX */
	ID3DXBuffer* pAdjacency = NULL;			// required for OptimizeInPlace
	ID3DXBuffer* pMaterials = NULL;			// materials for each subset
	ID3DXBuffer* pEffectsInstances = NULL;	// not interested in effects now
	DWORD dwNumMaterials = 0;

	/* Load the file into the mesh */
	D3DXLoadMeshFromX(cszFileName, D3DXMESH_MANAGED, m_pD3DDevice, &pAdjacency,
		&pMaterials, &pEffectsInstances, &dwNumMaterials, &pMesh);
	DWORD dError = ::GetLastError();

	/* What is the flexible vertex format of the vertex buffer before cloning? */
	DWORD dMeshFVF = pMesh->GetFVF();

	/* Clone the mesh, ensuring that it has room for position, normals, and texture coordinates */
	ID3DXMesh* pClone = NULL;
	HR(pMesh->CloneMeshFVF(D3DPOOL_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX2, m_pD3DDevice, &pClone));
	pMesh->Release();
	pMesh = pClone;

	/* Recompute the normals if they were not in the file */
	if (0 == (dMeshFVF & D3DFVF_NORMAL))
	{
		HR(D3DXComputeNormals(pMesh, (DWORD*)pAdjacency->GetBufferPointer()));
	}

	/* Optimize the mesh */
	HR(pMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE,
		(DWORD*)pAdjacency->GetBufferPointer(),
		0, 0, 0));

	/* Done with buffers */
	pAdjacency->Release();
	pEffectsInstances->Release();
	pMaterials->Release();
}

void Graphics::LoadTextureFromFile(const char* cszFileName, color* cKey, IDirect3DTexture9* & pTex)
{
	// determine whether or not the image has a color key, then set it
	D3DCOLOR colorKey = NULL;
	if(cKey != NULL)
		colorKey = D3DCOLOR_ARGB(255, cKey->red, cKey->green, cKey->blue);

	// use this function instead, it allows for more control in how it is setup
	D3DXCreateTextureFromFileEx(m_pD3DDevice,								// the device
		cszFileName,								// location of the file
		0,0,//D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, // prevents rounding to even binary numbers on dimensions
		0,											// default mip mapping
		0,											// use non-dynamic texturing
		D3DFMT_UNKNOWN,								// pixel formatting - takes directly from file
		D3DPOOL_MANAGED,							// manage the memory class for us
		D3DX_DEFAULT,								// how the image is filtered (normal)
		D3DX_DEFAULT,								// how the image is filtered (mip-filtering)
		colorKey,									// use the color key if there is one
		NULL,										// the image info, containing the texture's dimensions
		NULL,										// we don't need palette, so NULL is used
		&pTex);										// the texture to be received
}

void Graphics::SetPlayerLight(float x, float y, float z)
{
	m_PlayerLight.Position.x = x;
	m_PlayerLight.Position.y = y;
	m_PlayerLight.Position.z = z;
	D3DXVec3Normalize((D3DXVECTOR3*)&m_PlayerLight.Direction, (D3DXVECTOR3*)&m_PlayerLight.Position);
	m_pD3DDevice->SetLight(LIGHT_PLAYER, &m_PlayerLight);
}


void Graphics::SetCameraPos(float x, float y, float z)
{
	m_Camera.eyePos.x = x;
	m_Camera.eyePos.y = y;
	m_Camera.eyePos.z = z;
	RefreshCamera();
}


void Graphics::SetCameraView(float x, float y, float z)
{
	m_Camera.lookAt.x = x;
	m_Camera.lookAt.y = y;
	m_Camera.lookAt.z = z;
	RefreshCamera();
}

void Graphics::Release()
{
	if(!m_bInitialized || m_bInitFailed)
		return;

	if(m_pD3DDevice != NULL)
	{
		m_pD3DDevice->Release();
		m_pD3DDevice = NULL;
	}
	if(m_pFontManager != NULL)
	{
		m_pFontManager->Release();
		m_pFontManager = NULL;
	}
	if(m_pSpriteManager != NULL)
	{
		m_pSpriteManager->Release();
		m_pSpriteManager = NULL;
	}
}

Graphics::~Graphics()
{
	Release();
}